#include <mutex>
using namespace std;

/***************************
 * 单例模式：一个类只能创建一个对象
 * 饿汉单例：进程启动时就已经实例对象
 * 优点：1、没有线程安全问题
 * 缺点：1、一个程序中，如果有多个单例，并且有先后创建初始化的需求时，饿汉模式无法控制先后
 *		 （比如程序两个单例类A和B，假设要求A先创建初始化，B再创建初始化）
 *		2、饿汉单例类，当初始化任务多的时候，会影响程序启动速度。
 *
 */

#if 0
class Singleton
{
public:
    static Singleton* GetInstance()
    {
        return &instance;
    }
private:
    Singleton() {}
    ~Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static Singleton instance;
};
Singleton Singleton::instance; 
#endif

#if 0
/***************************
 * 懒汉单例：第一次使用对象时创建实例对象
 * 优点：1、当有多个单例模式类时，能够由用户自行控制先后实例初始化的顺序问题。
 *      2、不影响启动速度。
 * 缺点：1、存在线程安全问题（多执行流同时第一次获取实例对象时需要加锁）
 *		 
 */

std::mutex mtx;
class Singleton
{
public:
    // 不可重入函数
    static Singleton* GetInstance()   
    { 
        // 双重判断降低加锁粒度，外层用于判断是否需要加锁
        // 内层用于保证线程安全
        if(instance == nullptr)
        {
            // mtx.lock();
            lock_guard<std::mutex> lock(mtx);
            if(instance == nullptr)
            {
                instance = new Singleton();
            }
            // mtx.unlock();
        }
        return instance;
    }
private:
    Singleton() {}
    ~Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    static Singleton* volatile instance;
};
Singleton* volatile Singleton::instance = nullptr; 
#endif


/********************************
 *  线程安全的懒汉单例
 */
class Singleton
{
public:
    static Singleton* GetInstance()
    {
        // 通过静态变量初始化，在汇编指令上已经保证了线程互斥
        static Singleton instance; 
        return &instance;
    }
private:
    Singleton() {}
    ~Singleton() {}
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};
